home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d13
/
readcpm.arc
/
RCPM.ASM
< prev
next >
Wrap
Assembly Source File
|
1989-05-30
|
15KB
|
676 lines
INCLUDE C:\ASM\EQUATES.ASM
INCLUDE C:\ASM\MACROS.ASM
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG,DS:CSEG
ORG 0100H
;
; This equate controls where the files are written. If TO_Z80_DIR is EQUated
; to 1, then all files read from the floppy will be written to the path
; "C:\Z80". If TO_Z80_DIR is EQUated to 0, then all files will be written to
; the current default path.
;
TO_Z80_DIR EQU 1 ; Need to define it
DIR_ENTRY_SIZE EQU 32 ; 32 bytes for each dir entry
MAX_DIR_ENTRIES EQU 64 ; Max number of dir entries
NUM_TRACKS EQU 78 ; Number of tracks to read (* 2)
BLOCKS_PER_TRK EQU 2 ; Number of logical blks per trk
RECORDS_PER_BLK EQU 16 ; # of logical (128-byte) records
; per block
SECTORS_PER_BLK EQU 8 ; Number of physical (256-byte) sectors
; per blk
SECTOR_SIZE EQU 256 ; Size of disk sector
SHIFT EQU 8 ; Log2 of sector size
SPT EQU 16 ; Sectors per track
BYTES_PER_TRACK EQU SECTOR_SIZE*SECTORS_PER_BLK*BLOCKS_PER_TRK
RETRY_COUNT EQU 5 ; Retry count for floppy read
START PROC FAR
CALL INITIALIZE ; Set up
CALL LOAD_DISK ; Read entire floppy into memory
CALL WRITE_FILES ; Write files according to dir
MOV AX,4C00H ; Exit w/no error
INT 21H
START ENDP
;
; Once-only initialization. Reset SP, get current disk parameters, set up
; table of addresses for blocks & track
;
INITIALIZE PROC NEAR
POP SI ; Save return address
CLI ; Can't be interrupted...
LEA SP,STACK_TOP ; while SP is changed
STI ; Ints ok now
PUSH SI ; Put return address back
MOV AX,351EH ; Get INT 1EH vector. This points
INT 21H ; to the diskette parameters.
MOV OLD_DISK_PTR,BX
MOV OLD_DISK_PTR+2,ES ; Save old parameters...
PUSH DS ; Swap segments
PUSH ES
POP DS
POP ES
MOV SI,BX ; Point to the current parameters
LEA DI,DISK_TABLE ; And the ones I want to set
MOV CX,11 ; Copy old to new
REP MOVSB
PUSH CS
POP DS
MOV BYTE PTR BPS,SECTOR_SIZE/256 ; Reset bytes per sector
MOV BYTE PTR DTL,0FFH ; Still a mystery...
MOV BYTE PTR EOT,SPT ; Reset sectors per track
MOV BYTE PTR HST,0 ; Set to no head settle time
MOV BYTE PTR MST,0 ; ... and no motor start delay
SUB AX,AX ; BIOS function 0
INT 13H ; (reset diskette system)
LEA DI,BLOCKS ; Fill in the "BLOCKS" table
LEA AX,STACK_TOP ; Get last program addr
ADD AX,16 ; Align on a para boundary
MOV CL,4
SHR AX,CL
MOV CX,CS
ADD AX,CX ; Make it absolute
MOV CX,NUM_TRACKS*BLOCKS_PER_TRK
INIT_L1:
STOSW ; Put away a table entry
ADD AX,(BYTES_PER_TRACK/BLOCKS_PER_TRK)/16
LOOP INIT_L1
MOV TRACK_BUFFER,AX
RET
INITIALIZE ENDP
;
; Routine to load the entire floppy disk into RAM. Track-to-block translation
; is done once the track is in memory
;
LOAD_DISK PROC NEAR
MOV AX,CS
MOV DS,AX ; Make sure the segs are local
MOV ES,AX
LEA DX,PUT_IN_DISK ; Display prompt message
MOV AH,9
INT 21H
MOV AH,1 ; And read a character
INT 21H
CALL CRLF
LEA DX,LOADING_MESS ; Let me know what's going on...
MOV AH,9
INT 21H
LEA DI,BLOCKS ; Point to pointer table
MOV CX,NUM_TRACKS ; Number of tracks to read
;
; Note that track 0 & 1 (system tracks) are not read.
;
LD_L1:
CALL READ_SECTOR ; Read in a track
INC BYTE PTR CURR_HEAD ; Indicate next head
AND BYTE PTR CURR_HEAD,1 ; Mod 2
JNZ LD_L2 ; If changes from 1 to 0,
INC BYTE PTR CURR_TRACK ; go to next track
;
LD_L2:
MOV AX,1 ; Starting at sector 1,
CALL COPY_BLOCK ; copy from the track to a block
INC DI ; Point to next table entry
INC DI
MOV AX,SECTORS_PER_BLK+1 ; Now start at next block
CALL COPY_BLOCK ; And copy
INC DI
INC DI
MOV AL,'.' ; Tell me that 1 more has been read
CALL PCHAR
LOOP LD_L1 ; Do all tracks but 0
CALL CRLF
RET
LOAD_DISK ENDP
;
; Routine to copy from the track buffer to the block at (DI^)^. This routine
; assumes an interleave ("skew" under CP/M) of 3:1; i.e., to read a block
; starts at sector 1, the sectors must be read in this order:
; 1,4,7,10,13,16,3,6. A block that starts on sector 9: 9,12,15,2,5,8,11,14.
;
COPY_BLOCK PROC NEAR
SAVE_REG AX,CX,SI,DI,DS,ES
MOV DS,CS:TRACK_BUFFER ; Point to track buffer
MOV ES,CS:[DI] ; Need seg for this block
SUB DI,DI ; Copy to offset 0
MOV CX,SECTORS_PER_BLK ; This many sectors
CB_L1:
PUSH CX ; Save outer loop count
MOV SI,AX ; Get sector number
DEC SI ; ......
MOV CL,SHIFT ; ......
SHL SI,CL ; Make it an offset
MOV CX,SECTOR_SIZE ; Move one sector to block buffer
REP MOVSB
INC AX ; Calculate next sector to read
INC AX ; S = (S + 2)
AND AX,0FH ; S = ((S + 2) MOD 16)
INC AX ; S = ((S + 2) MOD 16) + 1
POP CX
LOOP CB_L1 ; Do all sectors in this block
RESTORE
RET
COPY_BLOCK ENDP
;
; Control routine to copy files from the block buffers to the appropriate DOS
; files according to the directory at BLOCK[0].
;
WRITE_FILES PROC NEAR
SAVE_REG AX,BX,CX,SI,DI,DS,ES
SUB BX,BX ; Set up parameters
MOV CX,MAX_DIR_ENTRIES ; Number of entries to scan
WF_L1:
PUSH CX ; Save the loop count
MOV DS,CS:BLOCKS ; Point to directory block
CMP BYTE PTR [BX],0E5H ; Is this a deleted file?
JE WF_CONT ; Continue the loop if it is
CMP BYTE PTR [BX+12],2 ; Is this a continuation extent?
JAE WF_CONT ; Continue the loop if it is
MOV CS:FILE_SIZE,0 ; Start with that record size
CALL COPY_NAME ; Copy the file name
PUSH CS
POP DS ; Reset to local seg
CALL ASK_ME ; Ask me if I want to copy it
CMP AL,'Y' ; If I said "N", then ignore it
JNE WF_CONT
CALL OPEN_FILE ; Open the output file
LEA DX,WRITING_MESS ; Tell me the file being written
MOV AH,9
INT 21H
LEA SI,FILE_NAME
CALL PSTRING ; Tell me the file name
CALL PUT_FILE ; Save the file
POP CX
PUSH CX
CALL FIND_OTHERS ; Find any other extents for that file
MOV AL,';' ; More info;
CALL PCHAR
MOV AL,SPACE
CALL PCHAR
MOV AX,CS:FILE_SIZE ; Show file size in 128-byte records
CALL PDECI
LEA SI,RECS_MESS
CALL PSTRING
CALL CLOSE_FILE ; Now close it
;
WF_CONT:
ADD BX,DIR_ENTRY_SIZE ; Point to next entry
POP CX ; Restore the loop count
LOOP WF_L1
RESTORE
RET
WRITE_FILES ENDP
;
; This routine displays a single file name (from the floppy directory), and
; prompts the user for a Y(es) or N(o) answer as to whether or not to copy
; the named file.
;
ASK_ME PROC NEAR
SAVE_REG DX,SI
LEA DX,WRITE_QUERY
MOV AH,9
INT 21H
LEA SI,NAME_BUF
CALL PSTRING
MOV AL,'?'
CALL PCHAR
MOV AL,SPACE
CALL PCHAR
CALL YES_OR_NO
RESTORE
RET
ASK_ME ENDP
YES_OR_NO PROC NEAR
MOV AH,7
MOV DL,0FFH
INT 21H
CMP AL,'a'
JB YORN_L1
CMP AL,'z'+1
JAE YORN_L1
SUB AL,32
YORN_L1:
CMP AL,'Y'
JE YORN_RET
CMP AL,'N'
JNE YES_OR_NO
YORN_RET:
CALL PCHAR
CALL CRLF
RET
YES_OR_NO ENDP
;
; Search the directory for any other entries matching the current entry,
; and write them to the file, too.
;
FIND_OTHERS PROC NEAR
SAVE_REG AX,BX,CX,SI,DI,DS,ES
MOV DS,CS:BLOCKS ; Point to the directory
MOV ES,CS:BLOCKS ; Twice
MOV DI,BX ; Get current entry
ADD DI,DIR_ENTRY_SIZE ; Point to next (to start)
DEC CX ; One less to search
FO_L1:
MOV SI,BX ; Point to the current one
INC SI ; Skip user number
PUSH DI ; Save pointer to other
CMP BYTE PTR ES:[DI],0E5H ; Is this a deleted entry?
JE FO_L2 ; If so, skip it
CMP BYTE PTR ES:[DI+12],1 ; Is this a starting extent?
JBE FO_L2 ; If so, skip it
INC DI ; Skip user number on this one
PUSH CX ; Save search count
MOV CX,11 ; Compare 11 bytes
REPE CMPSB ; Names match?
POP CX ; Get search count back
JNZ FO_L2 ; If no match, continue search
SUB DI,12 ; Point to name again
XCHG BX,DI ; Save and point at same time
CALL PUT_FILE ; Put these blocks in the file
XCHG BX,DI ; Get the dir ptr back
;
FO_L2:
POP DI ; Restore "other" pointer
ADD DI,DIR_ENTRY_SIZE ; Point to next
LOOP FO_L1 ; Loop away
RESTORE
RET
FIND_OTHERS ENDP
;
; Routine to copy the file name to ASCIIZ format for opening.
;
COPY_NAME PROC NEAR
SAVE_REG AX,CX,SI,DI,DS
MOV SI,BX ; Point to the dir entry
INC SI ; Skip the user number
LEA DI,NAME_BUF ; Point to the file name area
MOV CX,8 ; Max number to copy
CALL COPY_PART ; Copy the first part
MOV AL,'.' ; Put in the separator
STOSB
MOV CX,3 ; Max number of bytes in ext
CALL COPY_PART ; Copy it
SUB AL,AL ; Terminate with a null
STOSB
RESTORE
RET
COPY_NAME ENDP
;
; Routine to copy part of the file name from the dir entry to the local
; copy of the file name.
;
COPY_PART PROC NEAR
LODSB ; Pick up a byte
AND AL,7FH ; Don't care about 8th bit
CMP AL,SPACE ; Is it the end?
JE CN_L1
STOSB ; Put the byte away
CN_L1:
LOOP COPY_PART ; Loop for whatever
RET ; Return to caller
COPY_PART ENDP
;
; Routine to open the file @FILE_NAME
;
OPEN_FILE PROC NEAR
SAVE_REG AX,BX,CX,DX,DS
PUSH CS
POP DS
LEA DX,FILE_NAME ; Open the file @FILE_NAME
MOV AH,3CH
SUB CX,CX
INT 21H
JNC OPFL_L1 ; If error then let me know
PUSH AX
LEA DX,OPEN_ERROR
MOV AH,9
INT 21H ; Display "open error" message
LEA SI,FILE_NAME ; Display the file name
CALL PSTRING
MOV AL,':'
CALL PCHAR
POP AX
CALL PDECI
MOV AX,4C01H
INT 21H ; Exit w/error to DOS
;
OPFL_L1:
MOV HANDLE,AX ; Save the file handle
RESTORE
RET
OPEN_FILE ENDP
;
; Routine to close the current file
;
CLOSE_FILE PROC NEAR
SAVE_REG AX,BX
MOV AH,3EH
MOV BX,CS:HANDLE
INT 21H
RESTORE
RET
CLOSE_FILE ENDP
;
; Routine to copy the current block to the output file
;
SAVE_FILE PROC NEAR
SAVE_REG AX,BX,CX,DX
MOV BX,CS:HANDLE ; Get the handle to write to
MOV AX,CS:FILE_COUNT ; Get number of bytes to write
MOV CL,7
SHL AX,CL
MOV CX,AX
SUB DX,DX ; Write from offset 0...
MOV AH,40H
INT 21H
JNC SAVE_L1 ; If error occurred, tell me
PUSH CS
POP DS
LEA DX,ERROR_MESS
MOV AH,9
INT 21H
LEA SI,FILE_NAME
CALL PSTRING
CALL CLOSE_FILE ; Salvage what we can...
MOV AX,4C02H
INT 21H
;
SAVE_L1:
RESTORE
RET
SAVE_FILE ENDP
;
; Routine to control which blocks get copied to the output file. Reads the
; list of blocks in the current dir entry.
;
PUT_FILE PROC NEAR
SAVE_REG AX,CX,SI,DS
MOV DS,CS:BLOCKS
MOV SI,BX ; Point to the current dir entry
ADD SI,15 ; Point to the block list & size
MOV CL,[SI]
SUB CH,CH ; Get number of records for this extent
TEST BYTE PTR [SI-3],1 ; Is the extent number odd?
JZ PUTF_L11 ; If not, leave record count alone
ADD CX,80H ; If extent number is odd, then it
; means that there are 2 extents
; referenced by the dir entry, and
; the record count is for the 2nd
; extent; the first extent takes up
; 80H(128d) records. Adjust the
; record count accordingly.
PUTF_L11:
JCXZ PUTF_RET ; Return if size is 0
INC SI ; First block of file
MOV AH,16 ; Max number of blocks per extent
PUTF_L1:
LODSB ; Pick up the block number
OR AL,AL ; Is it 0?
JZ PUTF_RET ; Return if so
CMP CX,RECORDS_PER_BLK ; Are there at least that many left?
JB PUTF_L2 ; If not, just write that many.
SUB CX,RECORDS_PER_BLK
MOV CS:FILE_COUNT,RECORDS_PER_BLK
ADD CS:FILE_SIZE,RECORDS_PER_BLK
JMP SHORT PUTF_L3
PUTF_L2:
MOV CS:FILE_COUNT,CX
ADD CS:FILE_SIZE,CX
PUTF_L3:
PUSH AX ; Save the block number
PUSH SI
PUSH DS
SUB AH,AH
SHL AX,1
MOV SI,AX
MOV DS,CS:BLOCKS[SI]
CALL SAVE_FILE
POP DS
POP SI
POP AX
DEC AH
JNZ PUTF_L1 ; Loop for all blocks
;
PUTF_RET:
RESTORE
RET
PUT_FILE ENDP
;
; Simple utility routines called by most of the above
;
CRLF PROC NEAR
PUSH AX
MOV AL,CR
CALL PCHAR
MOV AL,LF
CALL PCHAR
POP AX
RET
CRLF ENDP
PDECI PROC NEAR
SAVE_REG AX,BX,DX
SUB BX,BX ; Put a flag word on the stack
PUSH BX
MOV BX,10 ; Divisor for decimal conversion
PDECI_L1:
SUB DX,DX ; Clear high word for division
DIV BX ; Divide number by 10
OR DL,30H ; Make the digit ASCII
PUSH DX ; And save it
OR AX,AX ; See if we're done yet
JNZ PDECI_L1 ; If not, keep going
PDECI_L2:
POP AX ; Get a previously saved digit
OR AX,AX ; See if it's the flag word
JZ PDECI_L3 ; If it is, we're done
CALL PCHAR ; Display the digit
JMP SHORT PDECI_L2 ; Do the rest
;
PDECI_L3:
RESTORE
RET
PDECI ENDP
PSTRING PROC NEAR
SAVE_REG AX,SI,DS
PUSH CS
POP DS ; Make sure seg is right
PSTR_L1:
LODSB ; Pick up a byte of the string
OR AL,AL ; Is it terminator?
JZ PSTR_RET ; If it is, exit
CALL PCHAR
JMP SHORT PSTR_L1
;
PSTR_RET:
RESTORE
RET
PSTRING ENDP
PCHAR PROC NEAR
SAVE_REG AX,DX
MOV DL,AL
MOV AH,2
INT 21H
RESTORE
RET
PCHAR ENDP
;
; Routine to read 1 track from the floppy
;
READ_SECTOR PROC NEAR
SAVE_REG AX,BX,CX,DX,ES
MOV AX,251EH
LEA DX,DISK_TABLE
INT 21H ; Set disk parameters
MOV CX,RETRY_COUNT ; Get the retry count
;
READ_SECTOR_L1:
PUSH CX ; Save current retry count
SUB DL,DL ; Always use A: drive
MOV DH,CS:CURR_HEAD ; Pick up BIOS parameters
MOV CL,1
MOV CH,CS:CURR_TRACK
MOV ES,CS:TRACK_BUFFER
SUB BX,BX
MOV AL,SPT
MOV AH,2 ; Read 1 track
INT 13H ; Via ROM BIOS
POP CX
JNC READ_SECTOR_L2 ; If no error, then finish
LOOP READ_SECTOR_L1 ; Otherwise, keep re-trying
LEA DX,FL_ERROR_MESS ; Display the error message
PUSH CS
POP DS
MOV AH,9
INT 21H
MOV DX,CS:OLD_DISK_PTR
MOV DS,CS:OLD_DISK_PTR+2
MOV AX,251EH
INT 21H
MOV AX,4C03H
INT 21H
;
READ_SECTOR_L2:
PUSH DS
MOV DX,CS:OLD_DISK_PTR ; Restore old parameters
MOV DS,CS:OLD_DISK_PTR+2
MOV AX,251EH
INT 21H
POP DS
RESTORE
RET
READ_SECTOR ENDP
DISK_TABLE DB 0
DB 0
DB 0
BPS DB 0
EOT DB 0
DB 0
DTL DB 0
DB 0
DB 0
HST DB 0
MST DB 0
OLD_DISK_PTR DW 0
DW 0
CURR_HEAD DB 0
CURR_TRACK DB 1
LOADING_MESS DB 'Reading diskette',CR,LF,'$'
PUT_IN_DISK DB 'Put CP/M diskette in drive A:, then press any key $'
OPEN_ERROR DB '? Error opening $'
WRITE_QUERY DB 'Copy $'
WRITING_MESS DB 'Writing $'
RECS_MESS DB ' records written',CR,LF,0
ERROR_MESS DB '? Error writing to $'
FL_ERROR_MESS DB '? Error reading floppy drive',CR,LF,'$'
IF TO_Z80_DIR
FILE_NAME DB 'C:\Z80\'
ELSE
FILE_NAME LABEL BYTE
ENDIF
NAME_BUF DB 13 DUP(0)
FILE_SIZE DW 0
HANDLE DW 0
TRACK_BUFFER DW 0
BLOCKS DW NUM_TRACKS*BLOCKS_PER_TRK DUP(0)
FILE_COUNT DW 0
FILE_BUFFER DW 0
EVEN
DW 256 DUP(0)
STACK_TOP = $
CSEG ENDS
END START